
	/***********************************************************************************************

		@API
		Class: 	Data.Blob

		Const:
			HEX

		Static:
			a			toArray			(bytes)
			s			toString		(bytes*, encode*)

		Generics:
			obj		Blob				(bytes*)
			obj		size				(n*)
			a			toArray			()
			s			toString		(encode*)
			s			valueOf			()

		Encoding:
			data	encode			(encoding)
			data	decode			(decoding)

		Movements:
			obj		seek				(ix)
			obj		rseek				(n)
			ix		index				()

		Operations:
			it		iterator		(n*)
			data	read				(bits*, ix*)
			obj		remove			(bits*, ix*)
			obj		clear				()

			obj		write				(data, bits*, ix*)
			obj		insert			(data, bits*, ix*)
			obj		append			(data, bits*)

		Note:
			HEX				code: 256
			bytes			al, s, Blob
			size			bytes
			toString	encode:	16
			data			i, al, s, Blob
			bits			default 8
			ix				byte index
			iterator	hasNext(bits*), next(bits*)

	***********************************************************************************************/

	//-------------------------------------------------------------------------------
	jsl.validator("Blob", function(v, rule, id, error) {
		if(v == null)										throw error("NullValue" , [ id ])
		if(!jsl.Data.Blob.isClassOf(v))	throw error("BadType" 	, [ id, "Blob" ])
	})
	//-------------------------------------------------------------------------------
	jsl.validator("Blob+", function(v, rule, id, error) {
		if(v == null)										throw error("NullValue" 	, [ id ])
		if(!jsl.Data.Blob.isClassOf(v))	throw error("BadType" 		, [ id, "Blob" ])
		if(!v.size()) 									throw error("e.blob.empty", [ id ])
	})
	//-------------------------------------------------------------------------------
	jsl.validator("binary", function(v, rule, id, error) {
		if(v == null)	throw error("NullValue", [ id ])

		if(	typeof v !== "string" &&
				!jsl.isArrayLike(v) 	&&
				!jsl.Data.Blob.isClassOf(v))	throw error("BadType", [ id, "Blob,string,array" ])

		new jsl.Data.Blob(v)
	})
	//-------------------------------------------------------------------------------
	jsl.validator("binary+", function(v, rule, id, error) {
		var bb

		if(v == null)	throw error("NullValue", [ id ])
		if(	typeof v !== "string" &&
				!jsl.isArrayLike(v) 	&&
				!jsl.Data.Blob.isClassOf(v)) throw error("BadType", [ id, "Blob,string,array" ])

		bb = new jsl.Data.Blob(v)
		if(!bb.size()) throw error("e.data.EmptyBinary", [ id ])
	})

	//-------------------------------------------------------------------------------
	jsl.Class("jsl.Data.Blob", {

		//static const
		//-------------------------------------------------------------------------------
		$HEX: "\u0100",

		//private data
		//-------------------------------------------------------------------------------
		$__m1: [], //hex -> n
		$__m2: [], //n		-> hex

		__bs: null,
		__ix:	0,

		//-------------------------------------------------------------------------------
		initializer: function() {
			var m1, m2, h

			m1 = this.__m1
			m2 = this.__m2

			for(var a = 0; a < 256; a++) {
				h = a.toString(16)
				h = h.length === 1 ? "0" + h : h
				m1[h] = a
				m2[a] = h
			}
		},

		//-------------------------------------------------------------------------------
		$toArray: function(bytes) {
			var v, sk, m1, bs, hex, a, l, ix, b

			//!
			if(arguments.length !== 1)	throw jsl.ParamError()
			jsl.validate(bytes,"t(s,al,Blob)", "bytes")//!

			if(jsl.isArray(bytes))					v = bytes
			else if(jsl.isArrayLike(bytes))	v = Array.prototype.slice.call(bytes)
			else if(this.isClassOf(bytes))	v = bytes.__bs.slice(), sk = true
			else {
				v 	= []
				hex	= bytes.charAt(0) === this.HEX

				if(hex) {//!
					if((ix = bytes.search(/[\s\S][^a-fA-F\d]/)) >= 0) throw jsl.ParamError("blob.BadByte:bytes[" + ix + "]")
					if(!(bytes.length % 2))														throw jsl.ParamError("blob.BadHexString:bytes")//!

					m1 = this.__m1
					bs = bytes.slice(1).toLowerCase().split("")

					for(a = 0, b = 0, l = bs.length; a < l; a += 2)
						v[b++] = m1[bs[a] + bs[a + 1]]
				}
				else {
					m1 = jsl.Data.__m1
					bs = bytes.split("")

					for(a = 0, l = bs.length; a < l; a++)
						v[a] = m1[bs[a]]
				}
			}

			//!
			if(!sk)
				for(a = 0, l = v.length; a < l; a++)
					if(!(v[a] >= 0 && v[a] < 256)) throw jsl.ParamError("blob.BadByte:bytes[" + a + "]")//!

			return v
		},
		//-------------------------------------------------------------------------------
		$toString: function(bytes, encode) {
			var v, s, ix, bs, a, l, m1, m2, bb, b, hex

			//!
			if(arguments.length > 2)		throw jsl.ParamError()
			if(arguments.length)				jsl.validate(bytes,"t(s,al,Blob)", "bytes")
			if(arguments.length === 2)	jsl.validate(encode, "ns(16)", "encode")//!

			//1-no arguments
			if(!arguments.length)
				v = jsl.__mlng_object_name(this)

			//2-hexadecimal string
			else if((s = jsl.isString(bytes)) && bytes.charAt(0) === this.HEX) {

				//!
				if((ix = bytes.search(/[\s\S][^a-fA-F\d]/)) >= 0) throw jsl.ParamError("blob.BadByte:bytes[" + (ix - 1) + "]")
				if(!(bytes.length % 2))														throw jsl.ParamError("blob.BadHexString:bytes")//!

				bs = bytes.slice(1)
				if(encode)
					v = bs + ""
				else {
					v 	= []
					m1	= this.__m1
					m2	= jsl.Data.__m2
					bs 	= bs.toLowerCase().replace(/[a-f\d]{2}/g, "$&*").split("*")
					bs.pop()

					for(a = 0, l = bs.length; a < l; a++)
						v[a] = m2[m1[bs[a]]]

					v = v.join("")
				}
			}

			//3-string
			else if(s) {//!
				if((ix = bytes.search(/[^\u0000-\u00FE]/)) > -1) throw jsl.ParamError("blob.BadByte:bytes[" + ix + "]")//!

				if(!encode)
					v = bytes + ""
				else {

					v 	= []
					bs 	= bytes.split("")
					m1	= jsl.Data.__m1
					m2	= this.__m2

					for(a = 0, l = bs.length; a < l; a++)
						v[a] = m2[m1[bs[a]]]

					v = v.join("")
				}
			}

			//4-array/Blob
			else {

				v 	= []
				b 	= 0
				bb	= jsl.Data.Blob.isClassOf(bytes)
				bs	= bb ? bytes.__bs : bytes

				//!
				if(!bb)
					for(a = 0, l = bs.length; a < l; a++)
						if(!(bs[a] >= 0 && bs[a] < 256)) throw jsl.ParamError("blob.BadByte:bytes[" + a + "]")//!

				if(encode) {

					hex	= "0123456789abcdef".split("")
					b		= 0

					for(a = 0, l = bs.length; a < l; a++) {
						v[b++] = hex[bs[a] >> 4]
						v[b++] = hex[bs[a] & 0xF]
					}
				}
				else {

					v		= []
					m2 	= jsl.Data.__m2

					for(a = 0, l = bs.length; a < l; a++)
						v[a] = m2[bs[a]]
				}

				v = v.join("")
			}

			return v
		},
		//-------------------------------------------------------------------------------
		Blob: function(bytes) {
			var v//!
			if(arguments.length > 1)	throw jsl.ParamError()//!

			if(arguments.length) {//!
				//t(s,al,Blob) instead of binary to avoid recursion error
				jsl.validate(bytes, "t(al,s,Blob)", "bytes")//!
				v = jsl.Data.Blob.toArray(bytes)
			}

			this.__bs = v || []
		},
		//-------------------------------------------------------------------------------
		size: function(n) {
			var bs, v//!
			if(arguments.length > 1)	throw jsl.ParamError()
			if(arguments.length)			jsl.validate(n, "i", "n")//!

			if(arguments.length) {
				bs = this.__bs
				if(n > bs.length)	for(var a = bs.length; a < n; a++) bs[a] = 0
				else							bs.length = n < 0 ? this.__bs.length + n : n
				v = this
			}
			else
				v = this.__bs.length

			return v
		},
		//-------------------------------------------------------------------------------
		toArray: function() {//!
			if(arguments.length)	throw jsl.ParamError()//!
			return jsl.Data.Blob.toArray(this)
		},
		//-------------------------------------------------------------------------------
		toString: function(encode) {
			//@sys: no parameters check
			//the toString can be called also on the prototype (ex: Browser console stringify all the properties
			//and therefore also __proto)
			if(this.__bs)
				if(arguments[0] == 16)	return jsl.Data.Blob.toString(this, 16)
				else										return jsl.Data.Blob.toString(this)

			return "jsl.Data.Blob"
		},
		//-------------------------------------------------------------------------------
		valueOf: function() {
			//@sys: no parameters check
			//this method is called also in blob + ""
			return this.toString()
		},
		//-------------------------------------------------------------------------------
		encode: function(encoding) {
			var data//!
			if(arguments.length !== 1)	throw jsl.ParamError()
			jsl.validate(encoding, "s+", "encoding")//!

			try 			{ data = jsl.Data.encode(this, encoding) }
			catch(e) 	{ throw (e instanceof jsl.ParamError ? jsl.Error(e).clearStackTrace() : e) }

			return data
		},
		//-------------------------------------------------------------------------------
		decode: function(decoding) {
			var data//!
			if(arguments.length !== 1)	throw jsl.ParamError()
			jsl.validate(decoding, "s+", "decoding")//!

			try 			{ data = jsl.Data.decode(this, decoding) }
			catch(e) 	{ throw (e instanceof jsl.ParamError ? jsl.Error(e).clearStackTrace() : e) }

			return data
		},
		//-------------------------------------------------------------------------------
		seek: function(ix) {
			var nix//!
			if(arguments.length !== 1)	throw jsl.ParamError()
			jsl.validate(ix, "i", "ix")//!

			nix = ix >= 0 ? +ix : this.__bs.length * 8 + ix

			//!
			if(nix < 0)	throw jsl.ParamError("blob.BadSeek:ix")//!

			this.__ix = nix
			return this
		},
		//-------------------------------------------------------------------------------
		rseek: function(n) {
			var nix//!
			if(arguments.length !== 1)	throw jsl.ParamError()
			jsl.validate(n, "i", "n")//!

			nix = this.__ix + n

			//!
			if(nix < 0)	throw jsl.ParamError("blob.BadSeek:n")//!

			this.__ix = nix
			return this
		},
		//-------------------------------------------------------------------------------
		index: function() {//!
			if(arguments.length)	throw jsl.ParamError()//!
			return this.__ix
		},
		//-------------------------------------------------------------------------------
		iterator: function(n) {
			var it, obj

			//!
			if(arguments.length > 1)		throw jsl.ParamError()
			if(arguments.length === 1)	jsl.validate(n, "i+", "n")//!

			it 	= { __ix: this.__ix, __ix2: n != null ? this.__ix + n : null, __name: "jsl.Data.BlobIterator" }
			obj	= this

 			//-------------------------------------------------------------------------------
			it.hasNext = function(bits) {
				try {//!
					if(arguments.length > 1)	throw jsl.ParamError()
					if(arguments.length)			jsl.validate(bits, "i++", bits)//!
					return this.__ix + (bits ? bits - 1 : 0) < (this.__ix2 || obj.__bs.length * 8)
				}
				catch(e) {
					jsl.throwError(e, this, arguments)
				}
			}
  		//-------------------------------------------------------------------------------
			it.next = function(bits) {
				try {
					var n, d//!
					if(arguments.length > 1) 															throw jsl.ParamError()
					if(arguments.length)																	jsl.validate(bits, "i++", "bits")
					if(this.__ix >= (this.__ix2 || obj.__bs.length * 8))	throw jsl.EndOfIterationError()//!

					n = bits || 8
					d = obj.read(n, this.__ix)

					this.__ix += n
					return d
				}
				catch(e) {
					jsl.throwError(e, this, arguments)
				}
			}

			return it
		},
		//-------------------------------------------------------------------------------
		read: function(bits, ix) {
			var i, nb, bs, o, v, n, a//!
			if(arguments.length > 2)		throw jsl.ParamError()
			if(arguments.length)				jsl.validate(bits	, "i++"	, "bits")
			if(arguments.length === 2)	jsl.validate(ix		, "i"		, "ix")//!

			i 	= ix == null 	? this.__ix 								: ix
			i		= i < 0 			? this.__bs.length * 8 + i 	: i
			nb	= bits || 8

			//!
			if(i < 0)													throw jsl.ParamError("BadValue:ix")
			if(i + nb > this.__bs.length * 8) throw jsl.ParamError("blob.EOB:bits")//!

			bs	= this.__bs
			o		= i % 8
			i		= ~~(i / 8)

			//1-simple number
			if(nb <= 32) {
				v = (bs[i] << 24 | bs[++i] << 16 | bs[++i] << 8 | bs[++i]) << o
				v = v >>> (32 - nb)
			}
			//2-array of bytes
			else {

				n = nb
				a = 0
				v = []

				while(n > 0) {
					v[a++] 	= (bs[i] << 8 | bs[++i]) << o >>> (n >= 8 ? 8 : 16 - n)
					n				= n - 8
				}
			}

			if(arguments.length < 2) this.__ix += nb
			return v
		},
		//-------------------------------------------------------------------------------
		write: function(data, bits, ix) {
			var isN, i, bs, o, nb, ds, m1, m2, a, l//!
			if(!arguments.length || arguments.length > 3)	throw jsl.ParamError()
			jsl.validate(data, "t(i,al,s,Blob)", "data")

			if(jsl.isNumber(data)) {	if(arguments.length > 1) jsl.validate(bits, "i[1,32]"	, "bits")
																if(arguments.length > 2) jsl.validate(ix	,	"i"				, "ix") }
			else 									 { 	if(arguments.length > 1) jsl.validate(bits, "i"				, "ix")
																if(arguments.length > 2) throw jsl.ParamError()
		 												 }//!

			isN	= jsl.isNumber(data)
			if(isN)	i = ix	 != null ? ix 	: this.__ix
			else		i = bits != null ? bits	: this.__ix

			i	= i < 0 ? this.__bs.length * 8 + i : i//!
			if(i < 0) throw jsl.ParamError("BadValue:ix")//!

			o		= i % 8
			i		= ~~(i / 8)
			bs	= this.__bs
			if(i >= bs.length)	for(a = bs.length; a <= i; a++) bs[a] = 0

			//1-simple number
			if(isN) {

				nb 	= bits || 8
				ds 	= data << 32 - nb
				l 	= nb >= 8 - o ? 8 - o : nb
				m1	= ~((1 << l) - 1 << 8 - o - l) & 0xFF
				m2	= ds >>> 32 - 8 + o

				do {
					bs[i]	= bs[i++] & m1 | m2 & ~m1
					nb 		= nb - l
					ds		= ds << l
					l 		= nb > 8 ? 8 : nb
					m1  	= (1 << 8 - l) - 1
					m2		=  ds >>> 24
				} while(nb > 0)

				if(ix == null) this.__ix += bits || 8
			}
			//2-array of bytes
			else {

				try 			{ ds = jsl.Data.Blob.toArray(data) 												}
				catch(e) 	{ /*!*/throw jsl.ParamError(jsl.Error(e).message())/*!*/	}

				m1 = (1 << 8 - o) - 1
				m2 = 0xFF - m1

				for(a = 0, l = ds.length; a < l; a++) {
					bs[i] = bs[i++] & m2 | ds[a] >>> o
					if(o) bs[i] = bs[i] & m1 | ds[a] << 8 - o & 0xFF
				}

				if(bits == null) this.__ix += ds.length * 8
			}

			return this
		},
		//-------------------------------------------------------------------------------
		insert: function(data, bits, ix) {
			var bs, isN, i0, i, i2, nb, m1, m2, l, d, n//!
			if(!arguments.length || arguments.length > 3)	throw jsl.ParamError()
			jsl.validate(data, "t(i,al,s,Blob)", "data")

			if(jsl.isNumber(data)) {	if(arguments.length > 1) jsl.validate(bits, "i[1,32]"	, "bits")
																if(arguments.length > 2) jsl.validate(ix	,	"i"				, "ix") }
			else 									 { 	if(arguments.length > 1) jsl.validate(bits, "i"				, "ix")
																if(arguments.length > 2) throw jsl.ParamError()
		 												 }//!

			bs	= this.__bs
			nb	= bs.length * 8
			isN	= jsl.isNumber(data)
			if(isN)	i0 = ix	 	!= null ? ix 		: this.__ix, l = bits || 8
			else		i0 = bits != null ? bits	: this.__ix, l = data.length * 8

			i0 = i0 < 0 ? this.__bs.length * 8 + i0 : i0//!
			if(i0 < 0) throw jsl.ParamError("BadValue:ix")//!

			if(i0 + l  <= nb)	i2 	= nb - 1	, i = i2 - l
			else if(i0 < nb)	n 	= bs[bs.length - 1]

			while(i >= i0) {
				m1	= i  % 8
				m2	= i2 % 8
				l 	= (m1 < m2 ? m1 : m2) + 1
				l		= i - l + 1 < i0 ? i - i0 + 1 : l
				d 	= (bs[~~(i / 8)] >>> 7 - m1 & ((1 << l) - 1)) << 7 - m2
				bs[~~(i2 / 8)] = bs[~~(i2 / 8)] & ~(((1 << l) - 1) << 7 - m2) | d
				i 	-= l
				i2	-= l
			}

			try 			{ this.write.apply(this, arguments) }
			catch(e) 	{ if(e instanceof jsl.ParamError)	throw e.clearStackTrace()
									else throw e }

			if(n != null)	bs[bs.length - 1] |= n >> (i0 + l) % 8
			return this
		},
		//-------------------------------------------------------------------------------
		remove: function(bits, ix) {
			var bs, l, i, i2, nB, nb, m1, m2, d, n//!
			if(arguments.length > 2)	throw jsl.ParamError()
			if(arguments.length)			jsl.validate(bits	, "i+", "bits")
			if(arguments.length > 1) 	jsl.validate(ix		, "i"	, "ix")//!

			bs 	= this.__bs
			l 	= bits || 8

			i = ix != null ? +ix : this.__ix
			i	= i < 0 ? this.__bs.length * 8 + i : i//!
			if(i < 0) throw jsl.ParamError("BadValue:ix")//!

			i2 	= i + l
			nB 	= (bs.length < ~~(i2 / 8) ? bs.length : ~~(i2 / 8)) - 1 - ~~(i / 8)
			nb 	= bs.length * 8

			if(nB > 0)
				bs.splice(~~(i / 8) + 1, nB),
				i2 -= nB * 8,
				nb -= nB * 8

			//1-shift
			if(i + l < nb) {

				while(i2 < nb) {
					m1	= 7 - i  % 8
					m2	= 7 - i2 % 8
					l 	= (m1 < m2 ? m1 : m2) + 1
					d		= (bs[~~(i2 / 8)] << i2 % 8 & 0xFF) >>> i % 8
					bs[~~(i / 8)] = bs[~~(i / 8)] & ~((1 << l) - 1 << 8 - l >> i % 8) | d
					i 	+= l
					i2	+= l
				}

				n = (bits || 8) - (nB > 0 ? nB : 0)
				if(n <= 8)	bs[bs.length - 1] &= ~((1 << n) - 1)
				else				bs[bs.length - 1] &= 0, bs[bs.length - 2] &= ~((1 << n - 8) - 1)
			}
			//2-no shift
			else if(i < nb) {
				n	 		= 7 - i % 8
				i 		= ~~(i / 8)
				bs[i] = bs[i] >>> n << n
			}

			for(var a = 0; a < nB; a++) bs.push(0)
			return this
		},
		//-------------------------------------------------------------------------------
		clear: function() {//!
			if(arguments.length)	throw jsl.ParamError()//!
			return this.size(0)
		},
		//-------------------------------------------------------------------------------
		append: function(data, bits) {//!
			if(!arguments.length)																throw jsl.ParamError()
			if(arguments.length > (jsl.isNumber(data) ? 2 : 1))	throw jsl.ParamError()//!
			this.__ix = this.__bs.length * 8

			try 			{ this.write.apply(this, arguments) }
			catch(e) 	{ if(e instanceof jsl.ParamError)	throw e.clearStackTrace()
									else throw e }
			return this
		}
	})
